﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.IO
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;

    /// <summary>Except for <see cref="Dispose"/>, forwards calls to all methods to the stream passed to the
    /// constructor.</summary>
    /// <remarks>When streams like <see cref="QuotedPrintableStream"/> and <see cref="Base64Stream"/> are disposed,
    /// their underlying stream is inevitably also disposed. This is rather inconvenient sometimes and can be prevented
    /// by wrapping the underlying stream into a <see cref="ForwardingStream"/> first.</remarks>
    /// <threadsafety static="true" instance="false"/>
    public sealed class ForwardingStream : NonSeekableStream
    {
        private Stream stream;

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>Initializes a new instance of the <see cref="ForwardingStream"/> class.</summary>
        /// <exception cref="ArgumentNullException"><paramref name="stream"/> is <c>null</c>.</exception>
        public ForwardingStream(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            this.stream = stream;
        }

        /// <summary>See <see cref="Stream.CanRead"/>.</summary>
        public sealed override bool CanRead
        {
            get { return (this.stream != null) && this.stream.CanRead; }
        }

        /// <summary>See <see cref="Stream.CanWrite"/>.</summary>
        public sealed override bool CanWrite
        {
            get { return (this.stream != null) && this.stream.CanWrite; }
        }

        /// <summary>See <see cref="Stream.Flush"/>.</summary>
        public sealed override void Flush()
        {
            this.AssertNotDisposed();
            this.stream.Flush();
        }

        /// <summary>See <see cref="Stream.Read(byte[], int, int)"/>.</summary>
        public sealed override int Read(byte[] buffer, int offset, int count)
        {
            this.AssertNotDisposed();
            return this.stream.Read(buffer, offset, count);
        }

        /// <summary>See <see cref="Stream.Write(byte[], int, int)"/>.</summary>
        public sealed override void Write(byte[] buffer, int offset, int count)
        {
            this.AssertNotDisposed();
            this.stream.Write(buffer, offset, count);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>Flushes the the underlying stream.</summary>
        /// <remarks>The underlying stream is deliberately not disposed, see class remarks.</remarks>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Dispose() must never throw.")]
        protected sealed override void Dispose(bool disposing)
        {
            try
            {
                if (disposing && (this.stream != null))
                {
                    this.stream.Flush();
                }
            }
            catch
            {
            }
            finally
            {
                this.stream = null;
                base.Dispose(disposing);
            }
        }
    }
}
